<template>

    <el-table class="my-tree-grid" :data="filterList" border style="width: 100%" :row-class-name="setClassName">
      <slot name="index"></slot>
      <el-table-column :label="labelText" min-width="500" class-name="tree-grid-column">
        <template slot-scope="scope">
          <span v-on:click.stop.prevent='toggleDisplay(scope.row)' :class="scope.row.classStyle">
            <i v-if='scope.row.hasChild' v-on:click.stop.prevent='toggleDisplay(scope.row)'
              :class="{'el-icon-caret-right':!scope.row.isSpand,
              'el-icon-caret-bottom':scope.row.isSpand}">
            </i>
            &nbsp;{{scope.row.label}}
          </span>
        </template>
      </el-table-column>
      <slot name="column"></slot>
    </el-table>

</template>

<script>
import Vue from "vue";
import {
  Table,
  TableColumn
} from "element-ui";
import _ from "lodash";

Vue.use(Table);
Vue.use(TableColumn);
export default {
  name: "MyTreeGrid",
  props: {
    tree: { // 树结构完整数据
      default: []
    },
    labelText: { // 树结构完整数据
      default: "名称"
    },
    list: { // 用于反馈给父组件的列表数据
      default: null
    },
    superid: { // 顶级节点id
      default: "0"
    },
    defaultProps: { // 默认属性字段
      // 对象或数组默认值必须从一个工厂函数获取
      default: function () {
        return { 
          id: "id",
          label: "label",
          pid: "pid"
        }
      }
    },
    expandAll: { // 展开所有，默认不召开
      default: false
    }
  },
  data() {
    return {
      srcList: [], // 树形数据转换成列表数据
      filterList: [],
      spandingRecord: [] // 记录展开的树
    };
  },
  watch: {
    tree: {
      handler: function () {
        this.srcList = this.transferToArray(this.tree, this.superid);
        this.filterList = [];
        let defaultList = _.filter(this.srcList, (o) => {
          return o.pid == this.superid;
        });
        _.forEach(defaultList, item => {
          let tmp = item;
          tmp.treeLevel = 0;
          tmp.isSpand = false;
          tmp.classStyle = [
            {"is-spand": 0},
            "margin-0",
            "menu-tree"
          ];
          this.filterList.push(tmp);
        });
        this.$emit("update:list", this.filterList); // 将列表数据反馈给父组件
        
        // 展开操作前的行数据
        if (this.spandingRecord && this.spandingRecord.length > 0) {
          let tmp = this.copyObject(this.spandingRecord);
          _.forEach(tmp, (item, index) => {
            this.toggleDisplay(item);
          });
        }
      },
      immediate: true
    }
  },
  methods: {
    setClassName(row) {
      return "tree-level-" + row.row.treeLevel;
    },
    // 将树形结构数据转化为一维数组
    transferToArray(tree, parentFullId = "") {
      let array = [];
      _.forEach(tree, (item) => {
        let tmp = this.copyObject(item);
        tmp.id = tmp[this.defaultProps.id] + ""; // 将其字符串化，以便后续的lodash方法能正确找到相关信息
        tmp.pid = tmp[this.defaultProps.pid] + ""; // 将其字符串化，以便后续的lodash方法能正确找到相关信息
        tmp.label = tmp[this.defaultProps.label] + ""; // 将其字符串化，以便后续的lodash方法能正确找到相关信息
        tmp.fullpid = parentFullId + ","; // 将其字符串化，以便后续的lodash方法能正确找到相关信息
        tmp.hasChild = false;
        tmp.children = null;
        if (item.children && item.children.length > 0) {
          tmp.hasChild = true;
          array.push(tmp);
          array = _.union(array, this.transferToArray(item.children, tmp.fullpid + tmp.id));
        } else {
          array.push(tmp);
        }
      });
      return array;
    },
    toggleDisplay(row) {
      let index = _.findIndex(this.filterList, {id: row.id}); // 当前点击数据在显展示列表中是第几行，用于插入被展开的数据的插入点
      row = _.find(this.filterList, { id: row.id }); // 在列表中获取点击的行，避免从不同地方进入到toggleDisplay方法时携带的row的属性不一致的问题
      row.isSpand = !row.isSpand;
      if (row && row.isSpand && row.hasChild) {
        // 展开
        let childList = _.filter(this.srcList, (o) => {
          return o.pid == row.id;
        });
        let list = [];
        let parent = _.find(this.srcList, { id: row.id });
        _.forEach(childList, item => {
          let tmp = this.copyObject(item);
          tmp.treeLevel = item.fullpid.replace(/^,|,$/g, "").replace(/\w*(,)\w*/g, "$1").length;
          tmp.isSpand = false;
          tmp.classStyle = [
            ["margin-" + item.fullpid.replace(/^,|,$/g, "").replace(/\w*(,)\w*/g, "$1").length], // 根据逗号数来确定左边空间大小
            {"is-spand": 0},
            "menu-tree"
          ];
          list.push(tmp);
        });
        this.filterList.splice(index + 1, 0, ...list);
      } else if (row && !row.isSpand && row.hasChild) {
        // 收缩
        this.filterList = _.filter(this.filterList, o => {
          return o.fullpid.indexOf(row.fullpid + row.id + ",") != 0;
        });
      }
      this.$emit("update:list", this.filterList); // 将列表数据反馈给父组件
      
      // 保存展开记录(数组包含：根节点到叶节点id，用逗号分隔)
      this.spandingRecord = _.filter(this.filterList, (o) => {
        return o.isSpand && o.pId + o.id; // 只有展开的节点才记录
      });
    },
    copyObject(value) {
      if (value) return JSON.parse(JSON.stringify(value));
      return {};
    }
  }
};
</script>
<style lang="scss">
.my-tree-grid {
  .el-table__row {
    transition: display .5s;
  }
  td.tree-grid-column {
    .margin-0 {
      margin-left: 0px;
      &.is-spand:after {
        height: 22px !important;
        position: absolute;
        left: -5px;
        bottom: -25px;
        border-left: 1px dotted black;
        top: 15px;
      }
    }
    @for $i from 1 through 20
    {
        .margin-#{$i}
        {
            margin-left:(20px * $i);
        }
    }
    overflow: overlay;
    display: block;
    padding: 0 !important;
    line-height: 100%;
    .cell {
      padding-left: 20px !important;
      text-align:left;
      overflow: hidden;
      height: 100%;
      line-height: 53px;
      &:before {
        content: '';
      }
      .menu-tree {
        padding-left: 5px;
        cursor: pointer;
        position: relative;
        color:#409eff;;
        .el-icon-caret-right, .el-icon-caret-bottom{
          position: absolute;
          left:-10px;
          top:0px;
          font-size: 16px;
        }
        &:not(.margin-0):before {
          content: '';
          height: 25px;
          position: absolute;
          left: -22px;
          border-left: 1px dotted #ccc;
          width: 10px;
          border-bottom: 1px dotted #ccc;
        }
        &:not(.margin-0):after {
          content: '';
          height: 25px;
          position: absolute;
          left: -22px;
          border-left: 1px dotted #ccc;
          top: 10px;
        }
        &.last:not(.margin-0):before {
          width: 18px;
        }
        @for $i from 1 through 20 {
          .margin-#{$i} {
              width:(20px * $i);
          }
        }
      }
    }
  }
}
</style>












